package com.soul.common.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper;
import com.baomidou.mybatisplus.core.conditions.SharedString;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.Query;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.Update;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.soul.common.bean.comm.BasePo;
import com.soul.common.mapper.CommMapper;
import com.soul.common.utils.FormatUtil;
import com.soul.common.utils.ListUtil;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * 公用抽象 业务层类 实现基本的公共业务
 * @author shelina
 * @version 1.0
 * @date 2022/8/10 11:04
 */
public abstract class BaseService<M extends CommMapper<T>, T extends BasePo> extends ServiceImpl<M,T>{
    @Autowired
    protected M mapper;

    private final LambdaQueryWrapper<T> queryWrapper = Wrappers.lambdaQuery(getTypeClass());

    private final LambdaUpdateWrapper<T> updateWrapper = Wrappers.lambdaUpdate(getTypeClass());

    public void setMapper(M mapper){
        this.mapper = mapper;
    }

    public Class<T> getTypeClass(){
        return (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
    }

    /**
     * 流式操作查询包装器
     */
    public class LambadaQueryConditionWrapper extends AbstractLambdaWrapper<T, LambadaQueryConditionWrapper> implements Query<LambadaQueryConditionWrapper, T, SFunction<T, ?>> {
        private SharedString sqlSelect;

        public LambadaQueryConditionWrapper() {
            this((T) null);
        }

        public LambadaQueryConditionWrapper(T entity) {
            this.sqlSelect = new SharedString();
            super.setEntity(entity);
            super.initNeed();
        }

        public LambadaQueryConditionWrapper(Class<T> entityClass) {
            this.sqlSelect = new SharedString();
            super.setEntityClass(entityClass);
            super.initNeed();
        }

        LambadaQueryConditionWrapper(T entity, Class<T> entityClass, SharedString sqlSelect, AtomicInteger paramNameSeq, Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias, SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {
            this.sqlSelect = new SharedString();
            super.setEntity(entity);
            super.setEntityClass(entityClass);
            this.paramNameSeq = paramNameSeq;
            this.paramNameValuePairs = paramNameValuePairs;
            this.expression = mergeSegments;
            this.sqlSelect = sqlSelect;
            this.paramAlias = paramAlias;
            this.lastSql = lastSql;
            this.sqlComment = sqlComment;
            this.sqlFirst = sqlFirst;
        }

        @SafeVarargs
        public final LambadaQueryConditionWrapper select(SFunction<T, ?>... columns) {
            if (ArrayUtils.isNotEmpty(columns)) {
                this.sqlSelect.setStringValue(this.columnsToString(false, columns));
            }

            return (LambadaQueryConditionWrapper)this.typedThis;
        }

        public LambadaQueryConditionWrapper select(Class<T> entityClass, Predicate<TableFieldInfo> predicate) {
            if (entityClass == null) {
                entityClass = this.getEntityClass();
            } else {
                this.setEntityClass(entityClass);
            }

            Assert.notNull(entityClass, "entityClass can not be null", new Object[0]);
            this.sqlSelect.setStringValue(TableInfoHelper.getTableInfo(entityClass).chooseSelect(predicate));
            return (LambadaQueryConditionWrapper)this.typedThis;
        }

        public String getSqlSelect() {
            return this.sqlSelect.getStringValue();
        }

        protected LambadaQueryConditionWrapper instance() {
            return new LambadaQueryConditionWrapper(this.getEntity(), this.getEntityClass(), (SharedString)null, this.paramNameSeq, this.paramNameValuePairs, new MergeSegments(), this.paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());
        }

        public void clear() {
            super.clear();
            this.sqlSelect.toNull();
        }

        public List<T> list(){
            return mapper.selectList(this);
        }
        public <P extends IPage<T>> P page(P page){
            return mapper.selectPage(page,this);
        }
        public T one(){
            return mapper.selectOne(this);
        }
        public Long count(){
            return mapper.selectCount(this);
        }
        public int delete(){
            return mapper.delete(this);
        }
        public boolean exists(){
            return mapper.exists(this);
        }

        public List<Map<String,Object>> listMap() {
            return mapper.selectMaps(this);
        }
        public <V> List<V> listField(Function<T,V> function) {
            return ListUtil.toValueList(list(),function);
        }
        public <V> List<V> listMap(Class<V> vClass) {
            List<Map<String, Object>> maps = listMap();
            return BeanUtil.copyToList(maps,vClass);
        }
        public <P extends IPage<Map<String,Object>>> P pageMap(P page){
            return mapper.selectMapsPage(page,this);
        }
        public <V,P extends IPage<Map<String,Object>>> IPage<V> pageMap(P page,Class<V> vClass){
            P p = pageMap(page);
            return p.convert(t->BeanUtil.copyProperties(t,vClass));
        }

    }

    /**
     * 流式操作查询包装器
     */
    public class LambadaUpdateConditionWrapper extends AbstractLambdaWrapper<T, LambadaUpdateConditionWrapper> implements Update<LambadaUpdateConditionWrapper, SFunction<T, ?>> {
        private final List<String> sqlSet;

        public LambadaUpdateConditionWrapper() {
            this((T)null);
        }

        public LambadaUpdateConditionWrapper(T entity) {
            super.setEntity(entity);
            super.initNeed();
            this.sqlSet = new ArrayList();
        }

        public LambadaUpdateConditionWrapper(Class<T> entityClass) {
            super.setEntityClass(entityClass);
            super.initNeed();
            this.sqlSet = new ArrayList();
        }

        LambadaUpdateConditionWrapper(T entity, Class<T> entityClass, List<String> sqlSet, AtomicInteger paramNameSeq, Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias, SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {
            super.setEntity(entity);
            super.setEntityClass(entityClass);
            this.sqlSet = sqlSet;
            this.paramNameSeq = paramNameSeq;
            this.paramNameValuePairs = paramNameValuePairs;
            this.expression = mergeSegments;
            this.paramAlias = paramAlias;
            this.lastSql = lastSql;
            this.sqlComment = sqlComment;
            this.sqlFirst = sqlFirst;
        }

        public LambadaUpdateConditionWrapper set(boolean condition, SFunction<T, ?> column, Object val, String mapping) {
            return (LambadaUpdateConditionWrapper)this.maybeDo(condition, () -> {
                String sql = this.formatParam(mapping, val);
                this.sqlSet.add(this.columnToString(column) + "=" + sql);
            });
        }

        public LambadaUpdateConditionWrapper setSql(boolean condition, String sql) {
            if (condition && StringUtils.isNotBlank(sql)) {
                this.sqlSet.add(sql);
            }

            return (LambadaUpdateConditionWrapper)this.typedThis;
        }

        public String getSqlSet() {
            return CollectionUtils.isEmpty(this.sqlSet) ? null : String.join(",", this.sqlSet);
        }

        protected LambadaUpdateConditionWrapper instance() {
            return new LambadaUpdateConditionWrapper(this.getEntity(), this.getEntityClass(), (List)null, this.paramNameSeq, this.paramNameValuePairs, new MergeSegments(), this.paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());
        }

        public void clear() {
            super.clear();
            this.sqlSet.clear();
        }
        public int update(T po){
            return mapper.update(po,this);
        }
    }
    /**
     * 获取查询包装器
     * @return
     */
    public LambdaQueryWrapper<T> lambadaQueryWrapper(){
        return Wrappers.lambdaQuery(getTypeClass());
    }
    /**
     * 获取自定义流式操作查询包装器
     * @return
     */
    public LambadaQueryConditionWrapper getLambadaQueryWrapper(){
        return new LambadaQueryConditionWrapper(getTypeClass());
    }
    /**
     * 获取自定义流式操作查询包装器
     * @return
     */
    public LambadaUpdateConditionWrapper getLambadaUpdateWrapper(){
        return new LambadaUpdateConditionWrapper(getTypeClass());
    }

    /**
     * 获取最大排序号
     * @return
     */
    public Long getMaxSort(){
        return getMaxSort(null);
    }

    /**
     * 获取最大排序号,区分 parentId
     * @return
     */
    public Long getMaxSort(Long parentId){
        QueryWrapper<T> wrapper = Wrappers.query();
        wrapper.select(" max(sort) as maxSort");
        wrapper.eq(ObjectUtil.isNotEmpty(parentId),"parent_id",parentId);
        Long count = this.getObj(wrapper, FormatUtil::toLong);
        if (count==null){
            count = 0L;
        }
        return count + 1;
    }

    /**
     * 获取最大排序号,区分 字段
     * @return
     */
    public <R> Long getMaxSort(SFunction<T,R> fun,R filed){
        QueryWrapper<T> wrapper = Wrappers.query();
        wrapper.select(" max(sort) as maxSort");
        LambdaQueryWrapper<T> lambda = wrapper.lambda();
        lambda.eq(ObjectUtil.isNotEmpty(filed),fun,filed);
        Long count = this.getObj(wrapper, FormatUtil::toLong);
        if (count==null){
            count = 0L;
        }
        return count + 1;
    }

}
